package com.senlingspace.payment.core.refund;

import com.alibaba.fastjson2.JSON;
import com.senlingspace.common.core.dingtalk.DingTalkService;
import com.senlingspace.common.core.domain.AjaxResult;
import com.senlingspace.common.enums.*;
import com.senlingspace.common.utils.DateUtils;
import com.senlingspace.common.utils.NumberGenerator;
import com.senlingspace.common.utils.StringUtils;
import com.senlingspace.common.utils.bean.BeanUtils;
import com.senlingspace.payment.lakala.bean.request.LakalaRefundRequest;
import com.senlingspace.payment.lakala.bean.response.LakalaRefundResponse;
import com.senlingspace.payment.lakala.service.LakalaRefundService;
import com.senlingspace.system.domain.SlkjOrder;
import com.senlingspace.system.domain.SlkjOrderStatusLog;
import com.senlingspace.system.domain.SlkjRefundRecord;
import com.senlingspace.system.domain.dto.RefundOrderDto;
import com.senlingspace.system.event.ActionEvent;
import com.senlingspace.system.mapper.SlkjOrderMapper;
import com.senlingspace.system.mapper.SlkjOrderStatusLogMapper;
import com.senlingspace.system.mapper.SlkjRefundRecordMapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


/**
 * 小程序退款策略
 *
 * @author lebronjames
 * @date 2024/07/27
 */
@AllArgsConstructor
@Slf4j
@Service("miniProgramRefundStrategy")
public class MiniProgramRefundStrategy implements RefundStrategy {

    private final SlkjOrderMapper slkjOrderMapper;
    private final SlkjRefundRecordMapper slkjRefundRecordMapper;
    private final LakalaRefundService lakalaRefundService;
    private final SlkjOrderStatusLogMapper slkjOrderStatusLogMapper;
    private final DingTalkService dingTalkService;

    private final ApplicationEventPublisher applicationEventPublisher;


    @Transactional(rollbackFor = Exception.class)
    @Override
    public AjaxResult refund(SlkjOrder order, Long customerPhone) {
        log.info("小程序退款: {}", order);

        String paymentStatus = order.getPaymentStatus();
        if (LakalaTradeStateEnum.REFUND.name().equals(paymentStatus)) {
            log.info("订单 [{}] 已退款，禁止重复退款！", order.getOrderNumber());
            return AjaxResult.error("订单已退款，禁止重复退款！");
        }

        if (LakalaPaymentTypeEnum.INCASH.name().equals(order.getPaymentType())) {
            return AjaxResult.error("现金支付订单，不支持线上退款！");
        }

        if (StringUtils.isBlank(order.getThirdOrderNumber())) {
            return AjaxResult.error("订单异常，请联系总部！");
        }

        if (!order.getPaymentStatus().equals(LakalaTradeStateEnum.SUCCESS.name())) {
            return AjaxResult.error("订单未支付，禁止退款！");
        }

        // TODO: 不管订单当前业务状态是什么，都支持退款
        // Integer orderType = order.getOrderType();
        // switch (OrderType.fromCode(orderType)) {
        //     case OT_101:
        //         // 对于票类订单，只有未核销的订单才支持退款
        //         if (!OrderBusinessStatus.UNREDEEMED.getCode().equals(order.getBusinessStatus())) {
        //             return AjaxResult.error("当前订单，不支持退款！");
        //         }
        //         break;
        //     case OT_102:
        //         // 对于商品订单，只有特定状态下的订单才支持退款
        //         GoodsStatusEnum goodsStatusEnum = GoodsStatusEnum.fromString(order.getGoodsStatus());
        //         if (goodsStatusEnum == null || !Arrays.asList(GoodsStatusEnum.PENDING_PAYMENT, GoodsStatusEnum.WAITING_FOR_SHIPPING, GoodsStatusEnum.WAITING_FOR_PICKUP).contains(goodsStatusEnum)) {
        //             return AjaxResult.error("当前订单，不支持退款！");
        //         }
        //         break;
        //     default:
        //         return AjaxResult.error("未知订单类型，不支持退款！");
        // }

        if (LakalaTradeStateEnum.SUCCESS.name().equals(paymentStatus)) {
            String refundNumber = NumberGenerator.getCharacters(20);

            LakalaRefundRequest refundRequest = new LakalaRefundRequest();
            refundRequest.setLogNo(order.getThirdOrderNumber());
            refundRequest.setOutTradeNo(refundNumber);
            // 对应拉卡拉 acc_settle_amount 账户端结算金额
            refundRequest.setRefundAmount(order.getActualAmount() + "");
            refundRequest.setRefundReason("用户小程序申请退款！");
            refundRequest.setShopId(order.getShopId());

            log.info("调用拉卡拉执行退款：{}", JSON.toJSONString(refundRequest));
            LakalaRefundResponse refundResponse = lakalaRefundService.refund(refundRequest);
            log.info("退款请求响应：{}", JSON.toJSONString(refundResponse));
            if (LakalaResponseCodeEnum.BBS00000.name().equals(refundResponse.getCode())) {
                log.info("订单 [{}] 退款成功！", order.getOrderNumber());

                SlkjOrder updateOrder = getUpdateOrder(order, customerPhone);
                log.info("更新订单状态：{}", JSON.toJSONString(updateOrder));
                int update = slkjOrderMapper.updateSlkjOrder(updateOrder);

                SlkjOrderStatusLog statusLog = getUpdateOrderStatusLog(order, customerPhone);
                log.info("更新订单状态日志：{}", JSON.toJSONString(statusLog));
                slkjOrderStatusLogMapper.insert(statusLog);

                LakalaRefundResponse.RespData respData = refundResponse.getRespData();
                SlkjRefundRecord refundRecord = getSlkjRefundRecord(order, customerPhone, refundNumber, respData, updateOrder);
                log.info("插入退款记录：{}", JSON.toJSONString(refundRecord));
                int inserted = slkjRefundRecordMapper.insertSlkjRefundRecord(refundRecord);
                if (update == 0 || inserted == 0) {
                    log.error("退单数据插入失败！：updateOrder={}, refundRecord={}", JSON.toJSONString(updateOrder), JSON.toJSONString(refundRecord));
                    log.error("这里需要手动处理数据，做数据补偿！！！");
                    dingTalkService.sendTextMessage("退单数据插入失败，参数信息：updateOrder=" + JSON.toJSONString(updateOrder) +
                            "，refundRecord=" + JSON.toJSONString(refundRecord) + "，需要将这些数据做手动补偿！", true);
                }

                log.info("小程序退款事件发布：{}", order);
                RefundOrderDto orderDto = new RefundOrderDto();
                BeanUtils.copyProperties(order, orderDto);
                applicationEventPublisher.publishEvent(ActionEvent.of(orderDto, "小程序退款-回退分账"));
                return AjaxResult.success("退款成功！");
            }

            log.error("订单 [{}] 退款失败：{}", order.getOrderNumber(), refundResponse.getCode() + ":" + refundResponse.getMsg());
            dingTalkService.sendTextMessage("订单[order_id=" + order.getOrderId() + "]退款失败！", true);
            return AjaxResult.error("退款失败（" + refundResponse.getCode() + "）!");
        }

        log.info("订单 [{}] 支付状态异常，非支付成功状态，禁止退款！", order.getOrderNumber());
        dingTalkService.sendTextMessage("订单[order_id=" + order.getOrderId() + "]支付状态异常，禁止退款！", true);
        return AjaxResult.error("订单支付状态异常，禁止退款！");
    }

    private static SlkjRefundRecord getSlkjRefundRecord(SlkjOrder order, Long customerPhone, String refundNumber, LakalaRefundResponse.RespData respData, SlkjOrder updateOrder) {
        SlkjRefundRecord refundRecord = new SlkjRefundRecord();
        refundRecord.setOrderId(order.getOrderId());
        refundRecord.setRefundNumber(refundNumber);
        refundRecord.setRefundAmount(order.getActualAmount());
        refundRecord.setRefundMethod("原路退回");
        refundRecord.setRefundStatus("REFUNDED");
        refundRecord.setRefundReason("用户小程序申请退款！");
        refundRecord.setThirdRefundNumber(respData.getLogNo());
        refundRecord.setRefundBy(customerPhone);
        refundRecord.setRefundTime(updateOrder.getUpdateTime());
        refundRecord.setAdditionalInfo(JSON.toJSONString(respData));
        refundRecord.setUpdateBy(null);
        refundRecord.setUpdateTime(null);
        return refundRecord;
    }

    private static SlkjOrderStatusLog getUpdateOrderStatusLog(SlkjOrder order, Long customerPhone) {
        SlkjOrderStatusLog orderStatusLog = new SlkjOrderStatusLog();
        orderStatusLog.setOrderId(order.getOrderId());
        orderStatusLog.setChangeBy(customerPhone);
        orderStatusLog.setChangeTime(DateUtils.getNowDate());
        orderStatusLog.setStatus(OrderBusinessStatus.REFUNDED.getCode());
        return orderStatusLog;
    }

    private static SlkjOrder getUpdateOrder(SlkjOrder order, Long customerPhone) {
        SlkjOrder updateOrder = new SlkjOrder();
        updateOrder.setOrderId(order.getOrderId());
        updateOrder.setPaymentStatus(LakalaTradeStateEnum.REFUND.name());
        updateOrder.setUpdateBy(customerPhone + "");
        updateOrder.setUpdateTime(DateUtils.getNowDate());
        switch (OrderType.fromCode(order.getOrderType())) {
            case OT_101:
                updateOrder.setBusinessStatus(OrderBusinessStatus.REFUNDED.getCode());
                break;
            case OT_102:
                updateOrder.setGoodsStatus(OrderBusinessStatus.REFUNDED.getCode());
                break;
            case OT_103:
                updateOrder.setBusinessStatus(OrderBusinessStatus.REFUNDED.getCode());
                updateOrder.setGoodsStatus(OrderBusinessStatus.REFUNDED.getCode());
                break;
        }

        if (!order.getOrderType().equals(OrderType.OT_102.getCode())) {
            updateOrder.setFeeAmount(0);
            updateOrder.setSettleAmount(0);
        }
        return updateOrder;
    }

}